/*
 * Copyright (c) 2015-2029, www.dibo.ltd (service@dibo.ltd).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.icesoft.system.auth.shiro;

import com.icesoft.framework.core.cache.RedisCacheManager;
import com.icesoft.framework.core.config.Cons;
import com.icesoft.framework.core.util.JSON;
import com.icesoft.framework.core.util.V;
import com.icesoft.framework.core.vo.JsonResult;
import com.icesoft.framework.core.vo.Status;
import com.icesoft.system.auth.entity.AuthToken;
import com.icesoft.system.auth.entity.LoginUser;
import com.icesoft.system.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.MediaType;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 无状态的访问控制过滤器
 *
 * @author JerryMa
 * @version v2.6.0
 * @date 2022/4/26
 * Copyright © diboot.com
 */
@Slf4j
@RequiredArgsConstructor
public class StatelessAccessControlFilter extends BasicHttpAuthenticationFilter {

	private final RedisCacheManager redisCacheManager;

	/**
	 * 判断是否登录
	 */
	@Override
	protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) {
		HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
		String authorization = httpRequest.getHeader("Authorization");
		if (V.isEmpty(authorization)) {
			log.debug("验证失败, uri={}", httpRequest.getRequestURI());
			return false;
		}
		String jwt = authorization.replace("Bearer ", "");
		if (V.isEmpty(jwt)) {
			log.debug("token: {} 验证失败, uri={}", jwt, httpRequest.getRequestURI());
			return false;
		}
		LoginUser<?> user = redisCacheManager.getCacheObject(jwt);
		if (!SecurityUtils.getSubject().isAuthenticated() && user != null) {
			AuthToken jwtToken = new AuthToken(jwt, user);
			jwtToken.setValidPassword(false);
			SecurityUtils.getSubject().login(jwtToken);
			log.debug("token: {} 保活完成, uri={}", jwt, httpRequest.getRequestURI());
		} else {
			JsonResult jsonResult = new JsonResult(Status.FAIL_INVALID_TOKEN);
			this.responseJson((HttpServletResponse) servletResponse, jsonResult);
			return false;
		}
		return true;
	}

	/**
	 * 没有登录的情况下会走此方法
	 *
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		log.debug("Token认证失败： onAccessDenied");
		JsonResult jsonResult = new JsonResult(Status.FAIL_INVALID_TOKEN);
		this.responseJson((HttpServletResponse) response, jsonResult);
		return false;
	}

	/***
	 * 返回json格式错误信息
	 * @param response
	 * @param jsonResult
	 */
	protected void responseJson(HttpServletResponse response, JsonResult jsonResult) {
		// 处理异步请求
		response.setStatus(401);
		response.setContentType(MediaType.APPLICATION_JSON_VALUE);
		response.setCharacterEncoding(Cons.CHARSET_UTF8);
		try (PrintWriter pw = response.getWriter()) {
			pw.write(JSON.stringify(jsonResult));
			pw.flush();
		} catch (IOException e) {
			log.error("处理异步请求异常", e);
		}
	}

	@Override
	protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
		return super.preHandle(request, response);
	}
}
